package com.aviatorscript.aviatorscript.engine;

import com.aviatorscript.aviatorscript.cache.RuleCacheManager;
import com.aviatorscript.aviatorscript.entity.FieldMappingRule;
import com.aviatorscript.aviatorscript.entity.TransformRule;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 数据转换引擎
 * 基于AviatorScript实现的数据转换核心引擎
 * 支持字段映射、脚本转换、类型转换等功能
 */
@Slf4j
@Component
public class DataTransformEngine {

    @Autowired
    private RuleCacheManager ruleCacheManager;

    /**
     * 脚本表达式缓存 - 避免重复编译脚本提高性能
     * Key: 脚本内容的hash值，Value: 编译后的表达式对象
     */
    private final ConcurrentHashMap<String, Expression> scriptCache = new ConcurrentHashMap<>();

    /**
     * 执行数据转换
     * @param ruleCode 转换规则编码
     * @param sourceData 源数据对象
     * @param targetClass 目标类型Class
     * @return 转换后的目标对象
     */
    public <T> T transform(String ruleCode, Object sourceData, Class<T> targetClass) {
        log.debug("开始执行数据转换，规则: {}, 目标类型: {}", ruleCode, targetClass.getName());
        
        try {
            // 获取转换规则
            TransformRule rule = ruleCacheManager.getRule(ruleCode);
            if (rule == null) {
                throw new RuntimeException("未找到转换规则: " + ruleCode);
            }

            // 获取字段映射规则
            List<FieldMappingRule> fieldMappings = ruleCacheManager.getFieldMappingRules(rule.getId());
            if (fieldMappings == null || fieldMappings.isEmpty()) {
                throw new RuntimeException("转换规则无字段映射配置: " + ruleCode);
            }

            // 创建目标对象实例
            T targetObject = targetClass.getDeclaredConstructor().newInstance();

            // 创建转换上下文，包含源数据和一些工具变量
            Map<String, Object> context = createTransformContext(sourceData);

            // 按排序顺序执行字段映射
            for (FieldMappingRule mapping : fieldMappings) {
                if (!mapping.getEnabled()) {
                    continue; // 跳过禁用的映射规则
                }

                try {
                    // 执行单个字段的转换
                    executeFieldMapping(sourceData, targetObject, mapping, context);
                } catch (Exception e) {
                    // 根据是否必填决定处理方式
                    if (mapping.getRequired()) {
                        throw new RuntimeException(
                            String.format("必填字段转换失败: %s -> %s, 错误: %s", 
                                mapping.getSourceFieldPath(), mapping.getTargetFieldPath(), e.getMessage()), e);
                    } else {
                        log.warn("非必填字段转换失败，跳过: {} -> {}, 错误: {}", 
                            mapping.getSourceFieldPath(), mapping.getTargetFieldPath(), e.getMessage());
                    }
                }
            }

            log.debug("数据转换完成，规则: {}", ruleCode);
            return targetObject;
            
        } catch (Exception e) {
            log.error("数据转换失败，规则: {}", ruleCode, e);
            throw new RuntimeException("数据转换失败: " + e.getMessage(), e);
        }
    }

    /**
     * 执行单个字段的映射转换
     * @param sourceData 源数据对象
     * @param targetObject 目标对象
     * @param mapping 字段映射规则
     * @param context 转换上下文
     */
    private void executeFieldMapping(Object sourceData, Object targetObject, 
                                   FieldMappingRule mapping, Map<String, Object> context) throws Exception {
        
        Object value = null;

        // 根据映射类型执行不同的转换逻辑
        switch (mapping.getMappingType()) {
            case FieldMappingRule.MappingType.DIRECT:
                // 直接映射：从源对象获取值
                value = getFieldValue(sourceData, mapping.getSourceFieldPath());
                break;
                
            case FieldMappingRule.MappingType.SCRIPT:
                // 脚本转换：使用AviatorScript执行转换
                value = executeScript(mapping.getTransformScript(), context);
                break;
                
            case FieldMappingRule.MappingType.CONSTANT:
                // 常量值：直接使用配置的常量
                value = mapping.getConstantValue();
                break;
                
            case FieldMappingRule.MappingType.FUNCTION:
                // 函数转换：调用预定义函数（暂未实现，可扩展）
                value = executeFunctionTransform(mapping, context);
                break;
                
            default:
                throw new RuntimeException("不支持的映射类型: " + mapping.getMappingType());
        }

        // 处理空值情况
        if (value == null) {
            // 使用默认值
            if (StringUtils.hasText(mapping.getDefaultValue())) {
                value = mapping.getDefaultValue();
            } else if (mapping.getRequired()) {
                throw new RuntimeException("必填字段不能为空: " + mapping.getTargetFieldPath());
            } else {
                return; // 非必填字段为空则跳过
            }
        }

        // 设置目标字段值
        setFieldValue(targetObject, mapping.getTargetFieldPath(), value, mapping.getTargetFieldType());
    }

    /**
     * 创建转换上下文
     * @param sourceData 源数据对象
     * @return 转换上下文Map
     */
    private Map<String, Object> createTransformContext(Object sourceData) {
        Map<String, Object> context = new HashMap<>();
        
        // 添加源数据对象
        context.put("source", sourceData);
        
        // 添加常用工具对象
        context.put("StringUtils", StringUtils.class);
        context.put("Math", Math.class);
        context.put("System", System.class);
        
        // 添加时间相关工具
        context.put("now", LocalDateTime.now());
        context.put("today", LocalDate.now());
        
        return context;
    }

    /**
     * 执行AviatorScript脚本
     * @param script 脚本内容
     * @param context 执行上下文
     * @return 脚本执行结果
     */
    private Object executeScript(String script, Map<String, Object> context) {
        if (!StringUtils.hasText(script)) {
            throw new RuntimeException("转换脚本不能为空");
        }

        try {
            // 尝试从缓存获取编译后的表达式
            Expression expression = scriptCache.get(script);
            if (expression == null) {
                // 编译脚本并缓存
                expression = AviatorEvaluator.compile(script);
                scriptCache.put(script, expression);
                log.debug("脚本编译并缓存: {}", script);
            }

            // 执行脚本
            Object result = expression.execute(context);
            log.debug("脚本执行成功: {} -> {}", script, result);
            return result;
            
        } catch (Exception e) {
            log.error("脚本执行失败: {}", script, e);
            throw new RuntimeException("脚本执行失败: " + e.getMessage(), e);
        }
    }

    /**
     * 执行函数转换（预留扩展点）
     * @param mapping 字段映射规则
     * @param context 转换上下文
     * @return 转换结果
     */
    private Object executeFunctionTransform(FieldMappingRule mapping, Map<String, Object> context) {
        // 这里可以实现预定义的转换函数
        // 例如：日期格式化、字符串处理、数值计算等
        throw new RuntimeException("函数转换功能暂未实现");
    }

    /**
     * 获取对象字段值（支持嵌套字段路径）
     * @param obj 对象
     * @param fieldPath 字段路径，支持点号分隔的嵌套路径
     * @return 字段值
     */
    private Object getFieldValue(Object obj, String fieldPath) throws Exception {
        if (obj == null || !StringUtils.hasText(fieldPath)) {
            return null;
        }

        String[] paths = fieldPath.split("\\.");
        Object current = obj;

        for (String path : paths) {
            if (current == null) {
                return null;
            }

            // 支持Map类型
            if (current instanceof Map) {
                current = ((Map<?, ?>) current).get(path);
            } else {
                // 反射获取对象字段值
                Field field = getField(current.getClass(), path);
                if (field == null) {
                    throw new RuntimeException("字段不存在: " + path + " in " + current.getClass().getName());
                }
                field.setAccessible(true);
                current = field.get(current);
            }
        }

        return current;
    }

    /**
     * 设置对象字段值（支持嵌套字段路径和类型转换）
     * @param obj 目标对象
     * @param fieldPath 字段路径
     * @param value 要设置的值
     * @param targetType 目标字段类型
     */
    private void setFieldValue(Object obj, String fieldPath, Object value, String targetType) throws Exception {
        if (obj == null || !StringUtils.hasText(fieldPath)) {
            return;
        }

        String[] paths = fieldPath.split("\\.");
        Object current = obj;

        // 处理嵌套路径
        for (int i = 0; i < paths.length - 1; i++) {
            Field field = getField(current.getClass(), paths[i]);
            if (field == null) {
                throw new RuntimeException("字段不存在: " + paths[i] + " in " + current.getClass().getName());
            }
            
            field.setAccessible(true);
            Object nextObj = field.get(current);
            
            // 如果嵌套对象为null，尝试创建实例
            if (nextObj == null) {
                nextObj = field.getType().getDeclaredConstructor().newInstance();
                field.set(current, nextObj);
            }
            
            current = nextObj;
        }

        // 设置最终字段值
        String finalFieldName = paths[paths.length - 1];
        Field targetField = getField(current.getClass(), finalFieldName);
        if (targetField == null) {
            throw new RuntimeException("字段不存在: " + finalFieldName + " in " + current.getClass().getName());
        }

        targetField.setAccessible(true);
        
        // 类型转换
        Object convertedValue = convertType(value, targetField.getType(), targetType);
        targetField.set(current, convertedValue);
    }

    /**
     * 类型转换
     * @param value 原始值
     * @param targetClass 目标类型
     * @param targetType 目标类型字符串描述
     * @return 转换后的值
     */
    private Object convertType(Object value, Class<?> targetClass, String targetType) {
        if (value == null) {
            return null;
        }

        // 如果类型已匹配，直接返回
        if (targetClass.isAssignableFrom(value.getClass())) {
            return value;
        }

        // 字符串转换
        if (targetClass == String.class) {
            return value.toString();
        }

        String strValue = value.toString();
        
        // 数值类型转换
        if (targetClass == Integer.class || targetClass == int.class) {
            return Integer.valueOf(strValue);
        }
        if (targetClass == Long.class || targetClass == long.class) {
            return Long.valueOf(strValue);
        }
        if (targetClass == Double.class || targetClass == double.class) {
            return Double.valueOf(strValue);
        }
        if (targetClass == BigDecimal.class) {
            return new BigDecimal(strValue);
        }
        if (targetClass == Boolean.class || targetClass == boolean.class) {
            return Boolean.valueOf(strValue);
        }

        // 日期时间类型转换
        if (targetClass == LocalDateTime.class) {
            return LocalDateTime.parse(strValue, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
        }
        if (targetClass == LocalDate.class) {
            return LocalDate.parse(strValue, DateTimeFormatter.ISO_LOCAL_DATE);
        }

        // 默认返回原值
        return value;
    }

    /**
     * 获取类的字段（包括父类字段）
     * @param clazz 类
     * @param fieldName 字段名
     * @return 字段对象
     */
    private Field getField(Class<?> clazz, String fieldName) {
        Class<?> current = clazz;
        while (current != null) {
            try {
                return current.getDeclaredField(fieldName);
            } catch (NoSuchFieldException e) {
                current = current.getSuperclass();
            }
        }
        return null;
    }

    /**
     * 清空脚本缓存
     */
    public void clearScriptCache() {
        scriptCache.clear();
        log.info("脚本缓存已清空");
    }

    /**
     * 获取脚本缓存统计信息
     * @return 缓存统计
     */
    public String getScriptCacheStatistics() {
        return String.format("脚本缓存数量: %d", scriptCache.size());
    }
} 